/**
 * Copyright 2014 xiaojian
 *
 *
 * Create on 2015年4月29日 下午3:19:11
 */
package xiaojian.toolkit.transport.codec;

import java.util.UUID;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import xiaojian.toolkit.bean.bytebean.core.BeanFieldCodec;
import xiaojian.toolkit.bean.xip.annotation.SsipSignal;
import xiaojian.toolkit.bean.xip.core.XipSignal;
import xiaojian.toolkit.transport.protocol.xip.XipHeader;
import xiaojian.toolkit.util.ByteUtils;

/**
 * @author <a href="mailto:caoxiaojian@wasu.cn">xiaojiancao</a>
 *
 */
public class MinaXipEncoder implements ProtocolEncoder {
	
	private static final Logger logger = LoggerFactory
			.getLogger(MinaXipEncoder.class);
	private int dumpBytes = 256;
	private boolean isDebugEnabled;
	private BeanFieldCodec xipBeanCodec;
	
	/**
	 * @return the xipBeanCodec
	 */
	public BeanFieldCodec getXipBeanCodec() {
		return xipBeanCodec;
	}

	/**
	 * @param xipBeanCodec
	 *            the xipBeanCodec to set
	 */
	public void setXipBeanCodec(BeanFieldCodec xipBeanCodec) {
		this.xipBeanCodec = xipBeanCodec;
	}
	
	private byte[] encodeXip(XipSignal signal) throws Exception {
		// once
		byte[] bytesBody = xipBeanCodec.encode(xipBeanCodec
				.getEncContextFactory().createEncContext(signal,
						signal.getClass(), null));

		SsipSignal attr = signal.getClass().getAnnotation(SsipSignal.class);
		if (null == attr) {
			throw new RuntimeException(
					"invalid ssip signal, bcs of no messageCode.");
		}

		XipHeader header = createHeader((byte) 1, signal.getIdentification(),
				attr.messageCode(), bytesBody.length);

		header.setTypeForClass(signal.getClass());

		byte[] bytes = ArrayUtils.addAll(xipBeanCodec.encode(xipBeanCodec
				.getEncContextFactory().createEncContext(header,
						XipHeader.class, null)), bytesBody);

		if (logger.isDebugEnabled() && isDebugEnabled) {
			logger.debug("encode XipMessage:" + signal);
			logger.debug("and XipMessage raw bytes -->");
			logger.debug(ByteUtils.bytesAsHexString(bytes, dumpBytes));
		}

		return bytes;
	}
	
	private XipHeader createHeader(byte basicVer, UUID id, int messageCode,
			int messageLen) {

		XipHeader header = new XipHeader();

		header.setTransaction(id);

		int headerSize = xipBeanCodec.getStaticByteSize(XipHeader.class);

		header.setLength(headerSize + messageLen);
		header.setMessageLength(messageLen);
		header.setMessageCode(messageCode);
		header.setBasicVer(basicVer);

		return header;
	}

	@Override
	public void encode(IoSession session, Object message,
			ProtocolEncoderOutput out) throws Exception {
		byte[] bytes = null;
		if (message instanceof XipSignal) {
			bytes = encodeXip((XipSignal) message);
		} else {
			throw new RuntimeException("encode: bean " + message
					+ " is not neigher XipSignal nor TLVSignal.");
		}
		if (null != bytes) {
			if (logger.isDebugEnabled()) {
				logger.debug("bean type {} and encode size is {}",
						message.getClass(), bytes.length);
			}
			out.write(IoBuffer.wrap(bytes));
		} else {
			logger.error("encode: " + message
					+ " can not generate byte stream.");
		}
	}
	
	public boolean isDebugEnabled() {
		return isDebugEnabled;
	}

	public void setDebugEnabled(boolean isDebugEnabled) {
		this.isDebugEnabled = isDebugEnabled;
	}

	public int getDumpBytes() {
		return dumpBytes;
	}

	/**
	 * @param dumpBytes
	 *            the dumpBytes to set
	 */
	public void setDumpBytes(int dumpBytes) {
		this.dumpBytes = dumpBytes;
	}

	@Override
	public void dispose(IoSession session) throws Exception {
		
	}

}
